﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace HY.Common
{
    public abstract class BindableBase : INotifyPropertyChanged
    {
        /// <summary>
        /// 属性值更改时发生
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 检查属性是否已与设置值相等，设置属性并仅在必要时通知侦听器。
        /// </summary>
        /// <typeparam name="T">属性的类型</typeparam>
        /// <param name="storage">对同时具有getter和setter的属性的引用</param>
        /// <param name="value">属性的所需值</param>
        /// <param name="propertyName">用于通知侦听器的属性的名称，此值是可选的，从支持CallerMemberName的编译器调用时可以自动提供。</param>
        /// <returns>如果值已更改，则为True；如果现有值与所需值匹配，则为false。</returns>
        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;

            storage = value;
            RaisePropertyChanged(propertyName);

            return true;
        }

        /// <summary>
        /// 检查属性是否已与设置值相等，设置属性并仅在必要时通知侦听器。
        /// </summary>
        /// <typeparam name="T">属性的类型</typeparam>
        /// <param name="storage">对同时具有getter和setter的属性的引用</param>
        /// <param name="value">属性的所需值</param>
        /// <param name="onChanged">属性值更改后调用的操作。</param>
        /// <param name="propertyName">用于通知侦听器的属性的名称，此值是可选的，从支持CallerMemberName的编译器调用时可以自动提供。</param>
        /// <returns>如果值已更改，则为True；如果现有值与所需值匹配，则为false。</returns>
        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;

            storage = value;
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);

            return true;
        }

        /// <summary>
        /// 引发此对象的PropertyChanged事件。
        /// <param name="propertyName">用于通知侦听器的属性的名称，此值是可选的，从支持CallerMemberName的编译器调用时可以自动提供。</param>
        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 引发此对象的PropertyChanged事件。
        /// </summary>
        /// <param name="args">PropertyChangedEventArgs参数</param>
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            PropertyChanged?.Invoke(this, args);
        }
    }
}
